<?php
namespace Entities;


abstract class ActiveEntity implements \ArrayAccess {
    const STATE_LOCKED = 1;

    private $_state;
    private $_metadata;
    static $_lockedObjects = array();
    protected static $_em;

    public static function setEntityManager(\Doctrine\ORM\EntityManager $em) {
        self::$_em = $em;
    }

    public function save($em = null) {
        $em = $em ? $em : self::$_em;
        $em->persist($this);
    }

    public function delete($em = null) {
        $em = $em ? $em : self::$_em;
        $em->remove($this);
    }

    public function __get($key) {
        return $this->get($key);
    }

    public function __set($key, $value) {
        $this->set($key, $value);
    }

    public function __isset($key) {
        return isset($this->$key);
    }

    public function offsetExists($key) {
        return isset($this->$key);
    }

    public function offsetGet($key) {
        return $this->get($key);
    }

    public function offsetSet($key, $value) {
        $this->set($key, $value);
    }

    public function offsetUnset($key) {
        unset($this->$key);
    }

    public function get($key) {
        $methodName = 'get' . ucfirst($key);

        return (is_callable(array($this, $methodName))) ? $this->$methodName() : $this->$key;
    }

    public function set($key, $value) {
        $methodName = 'set' . ucfirst($key);

        if (is_callable(array($this, $methodName))) {
            $this->$methodName($value);
        } else {
            $this->$key = $value;
        }
    }

    public function fromArray(array $array, $obj = null) {
        if ($obj === null) {
            $obj = $this;
        }

        foreach ($array as $key => $value) {
            if (is_array($value)) {
                $this->fromArray($value, $obj->$key);
            } else {
                $obj->set($key, $value);
            }
        }
    }

    public function toArray($obj = null) {
        if ($obj === null) {
            $obj = $this;
        }

        $array = array();

        if ($obj instanceof \DoctrineExtensions\ActiveEntity) {
            if ($obj->_state === self::STATE_LOCKED) {
                return array();
            }

            $originalState = $obj->_state;

            $reflFields = self::$_em->getClassMetadata(get_class($this))->reflFields;

            foreach ($reflFields as $name => $reflField) {
                $value = $this->$name;

                if ($value instanceof \DoctrineExtensions\ActiveEntity) {
                    $obj->_state = self::STATE_LOCKED;

                    if ($result = $value->toArray()) {
                        $array[$name] = $result;
                    }
                } else if ($value instanceof \Doctrine\Common\Collections\Collection) {
                    $obj->_state = self::STATE_LOCKED;

                    $array[$name] = $this->toArray($value);
                } else {
                    $array[$name] = $value;
                }
            }

            $obj->_state = $originalState;
        } else if ($obj instanceof \Doctrine\Common\Collections\Collection) {
            foreach ($obj as $key => $value) {
                if (in_array(spl_object_hash($obj), self::$_lockedObjects)) {
                    $array[$key] = $obj;
                    continue;
                }
                self::$_lockedObjects[] = spl_object_hash($obj);
                if ($result = $this->toArray($value)) {
                    $array[$key] = $result;
                }
            }
        }

        self::$_lockedObjects[] = array();
        return $array;
    }

    public function __toString() {
        return var_export($this->obtainIdentifier(), true);
    }

    public function obtainMetadata() {
        if (!$this->_metadata) {
            $this->_metadata = self::$_em->getMetadataFactory()->getMetadataFor(get_class($this));
        }

        return $this->_metadata;
    }

    public function obtainIdentifier() {
        return $this->obtainMetadata()->getIdentifierValues($this);
    }

    public function exists() {
        $id = self::$_em->getMetadataFactory()->getMetadataFor(get_class($this))->getIdentifierValues($this);

        return (self::$_em->contains($this) && !empty($id)) ? true : false;
    }

    public function __call($method, $arguments) {
        $func = substr($method, 0, 3);
        $fieldName = substr($method, 3, strlen($method));
        $fieldName = lcfirst($fieldName);

        if ($func == 'get') {
            return $this->$fieldName;
        } else {
            $this->$fieldName = $arguments[0];
        }
    }

    public static function __callStatic($method, $arguments) {
        return call_user_func_array(array(self::$_em->getRepository(get_called_class()), $method), $arguments);
    }

}
?>
